#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using std::cout;
using std::endl;

#define NUM 1024
#define SIZE 64
#define SEP " "

char get_in[NUM];
char *g_argv[SIZE]; // 指针数组
char g_env[64];

int main()
{
    while (1) // 模拟实现xshell,应该是一个永不推出的进程
    {
        // 1. 打印提示符
        cout << "[root@localhost myshell]# ";
        fflush(stdin);
        memset(get_in, 0, sizeof get_in);

        // 2. 获取用户的键盘输入
        if (fgets(get_in, sizeof get_in, stdin) == nullptr)
            continue;
        get_in[strlen(get_in) - 1] = 0;
        //cout << get_in << endl;

        // 3. 命令行字符串解析
        g_argv[0] = strtok(get_in, SEP);
        int index = 1;
        if (strcmp(g_argv[0], "ls") == 0)
            g_argv[index++] = (char*)"--color=auto"; // 上颜色
        if (strcmp(g_argv[0], "ll") == 0)     // 默认中是没有 ll 命令的,需要解析一下
        {
            g_argv[0] = (char*)"ls";
            g_argv[index++] = (char*)"-l";
            g_argv[index++] = (char*)"--color=auto";
        }
        while (g_argv[index++] = strtok(nullptr, SEP))
            ;                                                         // 继续往后解析
        if (strcmp(g_argv[0], "export") == 0 && g_argv[1] != nullptr) // 导入环境变量需要解析
        {
            strcpy(g_env, g_argv[1]);
            int ret = putenv(g_env);
            if (ret == 0)
                cout << "export success " << g_env << endl;
            continue;
        }

        // 4. 内置命令 cd命令需要主进程来完成,不可交给子进程
        if (strcmp(g_argv[0], "cd") == 0 && g_argv[1] != nullptr)
        {
            chdir(g_argv[1]);
            continue;
        }

        // 5. fork()
        pid_t id = fork();
        if (id == 0)
        {
            // 子进程
            cout << "下面功能是由子进程完成的: " << endl;
            execvp(g_argv[0], g_argv);
            exit(1);
        }
        // 父进程，阻塞等待即可
        int status = 0;
        pid_t ret = waitpid(-1, &status, 0);
        if (ret > 0)
        {
            cout << "等待子进程成功，退出码为: " << WEXITSTATUS(status) << endl;
        }
    }
    return 0;
}